<h1 id="typealgorithms">Type algorithms</h1>

<p>This section describes how type-related ECMAScript algorithms
like comparisons and coercions are extended to Duktape custom types.
Duktape specific type algorithms (<code>ToBuffer()</code> and <code>ToPointer()</code>)
are also discussed.</p>

<h2>Notation</h2>

<p>The following shorthand is used to indicate how values are compared:</p>

<table class="typeshorthand">
<thead>
<tr><th>Value</th><th>Description</th></tr>
</thead>
<tbody>
<tr><td>t</td><td>compares to true</td></tr>
<tr><td>f</td><td>compares to false</td></tr>
<tr><td>s</td><td>simple compare: boolean-to-boolean, string-to-string (string contents compared)</td></tr>
<tr><td>n</td><td>number compare: NaN values compare false, zeroes compare true regardless of sign (e.g. +0 == -0)</td></tr>
<tr><td>N</td><td>number compare in SameValue: NaN values compare true, zeroes compare with sign (e.g. SameValue(+0,-0) is false)</td></tr>
<tr><td>p</td><td>heap pointer compare</td></tr>
<tr><td>L</td><td>lightfunc compare: to be considered equal, Duktape/C function pointers and
                  internal control flags (including the "magic" value) must match</td></tr>
<tr><td>1</td><td>string vs. number: coerce string with ToNumber() and retry comparison</td></tr>
<tr><td>2</td><td>boolean vs. any: coerce boolean with ToNumber() to 0 or 1, and retry comparison</td></tr>
<tr><td>3</td><td>object vs. string/number: coerce object with ToPrimitive() and retry comparison</td></tr>
</tbody>
</table>

<p>Note that Boolean objects, String objects, and Number objects compare as any
other objects instead of being automatically unboxed.  For example, non-strict
equality compares plain string values with a byte-by-byte comparison, but String
objects are compared by object reference (like any other objects).</p>

<h2 id="non-strict-equality-algorithm">Equality (non-strict)</h2>

<p>Non-strict equality comparison is specified in
<a href="http://www.ecma-international.org/ecma-262/5.1/#sec-11.9.3">The Abstract Equality Comparison Algorithm</a>
for standard types.  Custom type behavior is as follows:</p>
<ul>
<li>Buffer: plain buffers are compared by their heap pointer (reference), no
    content comparison is done.  This matches Uint8Array behavior.</li>
<li>Pointer: comparison against any other type returns false.  Comparison to a
    pointer returns true if and only if the pointer values are the same.  Note
    in particular that comparing a number to a pointer returns false.  This seems
    a bit unintuitive, but numbers cannot represent 64-pointers accurately,
    comparing numbers and pointers might be error prone.</li>
<li>Lightfunc: comparison against any other type returns false.  Comparison
    to a lightfunc returns true if and only if both the Duktape/C function
    pointers and internal control flags (including the "magic" value) match.
    Note that a lightfunc never compares equal to an ordinary Function object,
    even when the Function object was created by coercing a lightfunc to an object.</li>
</ul>

<p>The standard behavior as well as behavior for Duktape custom types is summarized in the table below:</p>

<table class="typecomparison">
<thead>
<tr><th>   </th><th>und</th><th>nul</th><th>boo</th><th>num</th><th>str</th><th>obj</th><th>buf</th><th>ptr</th><th>lfn</th></tr>
</thead>
<tbody>
<tr><th>und</th><td>t  </td><td>t  </td><td>f  </td><td>f  </td><td>f  </td><td>f  </td><td>f  </td><td>f  </td><td>f  </td></tr>
<tr><th>nul</th><td>   </td><td>t  </td><td>f  </td><td>f  </td><td>f  </td><td>f  </td><td>f  </td><td>f  </td><td>f  </td></tr>
<tr><th>boo</th><td>   </td><td>   </td><td>s  </td><td>2  </td><td>2  </td><td>2  </td><td>f  </td><td>f  </td><td>f  </td></tr>
<tr><th>num</th><td>   </td><td>   </td><td>   </td><td>n  </td><td>1  </td><td>3  </td><td>f  </td><td>f  </td><td>f  </td></tr>
<tr><th>str</th><td>   </td><td>   </td><td>   </td><td>   </td><td>s  </td><td>3  </td><td>f  </td><td>f  </td><td>f  </td></tr>
<tr><th>obj</th><td>   </td><td>   </td><td>   </td><td>   </td><td>   </td><td>p  </td><td>f  </td><td>f  </td><td>f  </td></tr>
<tr><th>buf</th><td>   </td><td>   </td><td>   </td><td>   </td><td>   </td><td>   </td><td>p  </td><td>f  </td><td>f  </td></tr>
<tr><th>ptr</th><td>   </td><td>   </td><td>   </td><td>   </td><td>   </td><td>   </td><td>   </td><td>s  </td><td>f  </td></tr>
<tr><th>lfn</th><td>   </td><td>   </td><td>   </td><td>   </td><td>   </td><td>   </td><td>   </td><td>   </td><td>L  </td></tr>
</tbody>
</table>

<h2 id="strict-equality-algorithm">Strict equality</h2>

<p>Strict equality is much more straightforward and preferable whenever
possible for simplicity and performance.  It is described in
<a href="http://www.ecma-international.org/ecma-262/5.1/#sec-11.9.6">The Strict Equality Comparison Algorithm</a>
for standard types.  Custom type behavior is as follows:</p>
<ul>
<li>Buffer: like non-strict equality.</li>
<li>Pointer: like non-strict equality.</li>
<li>Lightfunc: like non-strict equality.</li>
</ul>

<p>The standard behavior as well as behavior for Duktape custom types is summarized in the table below:</p>

<table class="typecomparison">
<thead>
<tr><th>   </th><th>und</th><th>nul</th><th>boo</th><th>num</th><th>str</th><th>obj</th><th>buf</th><th>ptr</th><th>lfn</th></tr>
</thead>
<tbody>
<tr><th>und</th><td>t  </td><td>f  </td><td>f  </td><td>f  </td><td>f  </td><td>f  </td><td>f  </td><td>f  </td><td>f  </td></tr>
<tr><th>nul</th><td>   </td><td>t  </td><td>f  </td><td>f  </td><td>f  </td><td>f  </td><td>f  </td><td>f  </td><td>f  </td></tr>
<tr><th>boo</th><td>   </td><td>   </td><td>s  </td><td>f  </td><td>f  </td><td>f  </td><td>f  </td><td>f  </td><td>f  </td></tr>
<tr><th>num</th><td>   </td><td>   </td><td>   </td><td>n  </td><td>f  </td><td>f  </td><td>f  </td><td>f  </td><td>f  </td></tr>
<tr><th>str</th><td>   </td><td>   </td><td>   </td><td>   </td><td>s  </td><td>f  </td><td>f  </td><td>f  </td><td>f  </td></tr>
<tr><th>obj</th><td>   </td><td>   </td><td>   </td><td>   </td><td>   </td><td>p  </td><td>f  </td><td>f  </td><td>f  </td></tr>
<tr><th>buf</th><td>   </td><td>   </td><td>   </td><td>   </td><td>   </td><td>   </td><td>p  </td><td>f  </td><td>f  </td></tr>
<tr><th>ptr</th><td>   </td><td>   </td><td>   </td><td>   </td><td>   </td><td>   </td><td>   </td><td>s  </td><td>f  </td></tr>
<tr><th>lfn</th><td>   </td><td>   </td><td>   </td><td>   </td><td>   </td><td>   </td><td>   </td><td>   </td><td>L  </td></tr>
</tbody>
</table>

<h2 id="samevalue-algorithm">SameValue</h2>

<p>The <code>SameValue</code> algorithm is not easy to invoke from user code.
It is used by e.g. <code>Object.defineProperty()</code> when checking whether
a property value is about to change.  SameValue is even stricter than a
strict equality comparison, and most notably differs in how numbers are compared.
It is specified in
<a href="http://www.ecma-international.org/ecma-262/5.1/#sec-9.12">The SameValue algorithm</a>
for standard types.  Custom type behavior is as follows:</p>
<ul>
<li>Buffer: like non-strict (and strict) equality.</li>
<li>Pointer: like non-strict (and strict) equality.</li>
<li>Lightfunc: like non-strict (and strict equality).</li>
</ul>

<p>The standard behavior as well as behavior for Duktape custom types is summarized in the table below:</p>

<table class="typecomparison">
<thead>
<tr><th>   </th><th>und</th><th>nul</th><th>boo</th><th>num</th><th>str</th><th>obj</th><th>buf</th><th>ptr</th><th>lfn</th></tr>
</thead>
<tbody>
<tr><th>und</th><td>t  </td><td>f  </td><td>f  </td><td>f  </td><td>f  </td><td>f  </td><td>f  </td><td>f  </td><td>f  </td></tr>
<tr><th>nul</th><td>   </td><td>t  </td><td>f  </td><td>f  </td><td>f  </td><td>f  </td><td>f  </td><td>f  </td><td>f  </td></tr>
<tr><th>boo</th><td>   </td><td>   </td><td>s  </td><td>f  </td><td>f  </td><td>f  </td><td>f  </td><td>f  </td><td>f  </td></tr>
<tr><th>num</th><td>   </td><td>   </td><td>   </td><td>N  </td><td>f  </td><td>f  </td><td>f  </td><td>f  </td><td>f  </td></tr>
<tr><th>str</th><td>   </td><td>   </td><td>   </td><td>   </td><td>s  </td><td>f  </td><td>f  </td><td>f  </td><td>f  </td></tr>
<tr><th>obj</th><td>   </td><td>   </td><td>   </td><td>   </td><td>   </td><td>p  </td><td>f  </td><td>f  </td><td>f  </td></tr>
<tr><th>buf</th><td>   </td><td>   </td><td>   </td><td>   </td><td>   </td><td>   </td><td>p  </td><td>f  </td><td>f  </td></tr>
<tr><th>ptr</th><td>   </td><td>   </td><td>   </td><td>   </td><td>   </td><td>   </td><td>   </td><td>s  </td><td>f  </td></tr>
<tr><th>lfn</th><td>   </td><td>   </td><td>   </td><td>   </td><td>   </td><td>   </td><td>   </td><td>   </td><td>L  </td></tr>
</tbody>
</table>

<h2 id="type-conversion-and-testing">Type conversion and testing</h2>

<p>The custom types behave as follows for ECMAScript coercions described
<a href="http://www.ecma-international.org/ecma-262/5.1/#sec-9">Type Conversion and Testing</a>
(except SameValue which was already covered above):</p>

<table>
<thead>
<tr><th> </th><th>buffer</th><th>pointer</th><th>lightfunc</th></tr>
</thead>
<tbody>
<tr><th>DefaultValue</th><td>Usually <code>"[object Uint8Array]"</code>; like Uint8Array</td><td>TypeError</td><td><code>"light_&lt;PTR&gt;_&lt;FLAGS&gt;"</code> (toString/valueOf)</td></tr>
<tr><th>ToPrimitive</th><td>Usually <code>"[object Uint8Array]"</code>; like Uint8Array</td><td>identity</td><td><code>
"light_&lt;PTR&gt;_&lt;FLAGS&gt;"</code> (toString/valueOf)</td></tr>
<tr><th>ToBoolean</th><td>true</td><td>false for NULL pointer, true otherwise</td><td>true</td></tr>
<tr><th>ToNumber</th><td>ToNumber(String(buffer)), usually ToNumber("[object Uint8Array]") = NaN</td><td>0 for NULL pointer, 1 otherwise</td><td>NaN</td></tr>
<tr><th>ToInteger</th><td>same as ToNumber; usually 0</td><td>same as ToNumber</td><td>0</td></tr>
<tr><th>ToInt32</th><td>same as ToNumber; usually 0</td><td>same as ToNumber</td><td>0</td></tr>
<tr><th>ToUint32</th><td>same as ToNumber; usually 0</td><td>same as ToNumber</td><td>0</td></tr>
<tr><th>ToUint16</th><td>same as ToNumber; usually 0</td><td>same as ToNumber</td><td>0</td></tr>
<tr><th>ToString</th><td>Usually <code>[object Uint8Array]</code>; like Uint8Array</td><td><code>sprintf()</code> with <code>%p</code> format (platform specific)</td><td><code>"light_&lt;PTR&gt;_&lt;FLAGS&gt;"</code></td></tr>
<tr><th>ToObject</th><td>Uint8Array object (backs to argument plain buffer)</td><td>Pointer object</td><td>Function object</td></tr>
<tr><th>CheckObjectCoercible</th><td>allow (no error)</td><td>allow (no error)</td><td>allow (no error)</td></tr>
<tr><th>IsCallable</th><td>false</td><td>false</td><td>true</td></tr>
<tr><th>SameValue</th><td>(covered above)</td><td>(covered above)</td><td>(covered above)</td></tr>
</tbody>
</table>

<p>When a buffer is string coerced it behaves like an Uint8Array, with the
result usually being <code>"[object Uint8Array]"</code>.  This behavior was
changed in Duktape 2.0.  To create a string from buffer contents you can use
e.g. the Node.js Buffer binding or the Encoding API.</p>

<p>When a buffer is object coerced a new Uint8Array object is created, with
a new ArrayBuffer backing to the plain buffer (no copy is made).</p>

<p>When a lightfunc is coerced with ToPrimitive() it behaves like an ordinary
function: it gets coerced with <code>Function.prototype.toString()</code> with
the result (normally) being the same as ToString() coercion.</p>

<p>When a lightfunc is object coerced, a new Function object is created
and the virtual properties (<code>name</code> and <code>length</code> and
the internal "magic" value are copied over to the Function object.</p>

<h2>Custom coercions (ToBuffer, ToPointer)</h2>

<p>ToBuffer() coercion is used when a value is forced into a buffer
type e.g. with the <code>duk_to_buffer()</code> API call.  The coercion
is as follows:</p>
<ul>
<li>A buffer coerces to itself (identity).  The same buffer value is
    returned.</li>
<li>Any other type (including pointer and lightfunc) is first string coerced with
    <a href="http://www.ecma-international.org/ecma-262/5.1/#sec-9.8">ToString</a>,
    and the resulting string is then copied, byte-by-byte, into a
    fixed-size buffer.</li>
</ul>

<p>ToPointer() coercion is used e.g. by the <code>duk_to_pointer()</code>
call.  The coercion is as follows:</p>
<ul>
<li>A pointer coerces to itself.</li>
<li>Heap-allocated types (string, object, buffer) coerce to a pointer value
    pointing to their <b>internal heap header</b>.  This pointer has only a
    diagnostic value.  Note, in particular, that the pointer returned for a
    buffer or a string <b>does not</b> point to the buffer/string data area.
    (This coercion is liable to change even in minor versions, avoid relying
    on it.)</li>
<li>Any other types (including number) coerce to a NULL pointer.</li>
<li>Lightfunc coerces to a NULL pointer.  This is the case because C function
    pointers cannot be coerced to a <code>void *</code> in a portable manner.</li>
</ul>

<p>The following table summarizes how different types are handled:</p>

<table>
<thead>
<tr><th> </th><th>ToBuffer</th><th>ToPointer</th></tr>
</thead>
<tbody>
<tr><th>undefined</th><td>buffer with "undefined"</td><td>NULL</td></tr>
<tr><th>null</th><td>buffer with "null"</td><td>NULL</td></tr>
<tr><th>boolean</th><td>buffer with "true" or "false"</td><td>NULL</td></tr>
<tr><th>number</th><td>buffer with string coerced number</td><td>NULL</td></tr>
<tr><th>string</th><td>buffer with copy of string data</td><td>ptr to heap hdr</td></tr>
<tr><th>object</th><td>buffer with ToString(value)</td><td>ptr to heap hdr</td></tr>
<tr><th>buffer</th><td>identity</td><td>ptr to heap hdr</td></tr>
<tr><th>pointer</th><td><code>sprintf()</code> with <code>%p</code> format (platform specific)</td><td>identity</td></tr>
<tr><th>lightfunc</th><td>buffer with ToString(value)</td><td>NULL</td></tr>
</tbody>
</table>

<div class="note">
There is currently no ToLightFunc() coercion.  Lightfuncs can only be created
using the Duktape C API.
</div>

<h2>Addition</h2>

<p>The ECMAScript addition operator is specified in
<a href="http://www.ecma-international.org/ecma-262/5.1/#sec-11.6.1">The Addition operator (+)</a>.
Addition behaves specially if either argument is a string: the other argument
is coerced to a string and the strings are then concatenated.  This behavior
is extended to custom types as follows:</p>
<ul>
<li>As for standard types, object values are first coerced with <code>ToPrimitive()</code>;
    plain buffers and lightfuncs are normally coerced with <code>ToString()</code>.
    For plain buffers the result is usually <code>"[object Uint8Array]"</code>
    and for lightfuncs <code>"[object Function]"</code>.</li>
<li>Pointer values fall into the default number addition case.  They are coerced
    with <code>ToNumber()</code> and then added as numbers.  NULL pointers coerce to
    0, non-NULL pointers to 1, so addition results may not be very intuitive.</li>
</ul>

<p>Addition is not generally useful for custom types.  For example, if two plain
buffers are added, the result is usually <code>"[object Uint8Array][object Uint8Array]"</code>,
which matches how standard addition behaves for two Uint8Array instances.</p>

<h2>Property access</h2>

<p>If a plain buffer or pointer is used as a property access base value,
properties are looked up from the (initial) built-in prototype object
(<code>Uint8Array.prototype</code> or <code>Duktape.Pointer.prototype</code>).
This mimics the behavior of standard types.</p>

<p>For example:</p>
<pre>
duk&gt; buf = Duktape.dec('hex', '414243');  // plain buffer
= ABC
duk&gt; buf.subarray();
= function subarray() {"native"}
duk&gt; typeof buf.toString();
= string
</pre>

<p>Lightfuncs have a few non-configurable and non-writable virtual properties
(<code>name</code> and <code>length</code>) and inherit their remaining
properties from <code>Function.prototype</code>, which allows ordinary inherited
Function methods to be called:</p>
<pre class="ecmascript-code">
var bound = myLightFunc.bind('dummy', 123);
</pre>
